<template>
  <div class="page-container">
    <div id="app">
      <div
        ref="contentBox"
        :style="{ height: contentHeight + 'px' }"
        class="content-box"
        @scroll="onScroll">
        <div :style="{ height: realListBoxHeight }">
          <div :style="{ paddingTop: renderTop + 'px' }">
            <div
              v-for="item in renderList"
              ref="boxItem"
              :class="`box-item ${item.id % 2 === 0 ? 'bg' : ''}`"
              :style="{ height: itemHeight + 'px', lineHeight: itemHeight + 'px' }"
              :key="item.id">
              {{ item }}
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'VirtualList', // 虚拟列表
  data: function() {
    return {
      contentHeight: 400, // 父容器高度
      itemHeight: 40, // 每项数据高度
      realList: [], // 源数据列表
      renderList: [], // 渲染数据列表
      scrollTop: 0, // 滚动条卷起的高度
      startIndex: 0, // 起始索引
      endIndex: 0, // 结束索引
      renderTop: 0, // 渲染区域距离顶部的高度
      bufferNumber: 3, // 缓冲个数
    };
  },
  computed: {
    realListBoxHeight() { // 真实数据的盒子高度
      return (this.itemHeight * this.realList.length) + 'px';
    },
    renderNum() { // 渲染数据的个数
      return Math.ceil(this.contentHeight / this.itemHeight);
    },
    bufferArea() { // 缓冲区域 的高度
      return this.itemHeight * this.bufferNumber;
    }
  },
  mounted() {
    this.getList();
  },
  methods: {
    getList() {
      const arr = [];
      const length = 100;
      for (let i = 0; i < length; i++) {
        arr[i] = {
          index: i,
          id: i + 1,
          content: '列表数据内容' + (i + 1),
        };
      }
      this.realList = arr;
      this.onScroll();
    },
    onScroll() {
      this.scrollTop = this.$refs.contentBox.scrollTop;
      const state = this.scrollTop > this.bufferArea;
      this.startIndex = state ? Math.ceil((this.scrollTop - this.bufferArea) / this.itemHeight) : 0;
      this.endIndex = this.startIndex + this.renderNum + this.bufferNumber * 2;
      this.renderTop = state ? this.startIndex * this.itemHeight : 0;
      this.renderList = this.realList.slice(this.startIndex, this.endIndex);
    },
  }
};
</script>

<style scoped lang="scss">
.content-box {
  width: 500px;
  border: 1px solid #e4e7ed;
  overflow-y: auto;
  box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
  margin: 0 auto;
}
.box-item {
  padding: 0 20px;
  color: #606266;
  white-space: nowrap;
  overflow-x: scroll;
}
.bg {
  background-color: skyblue;
}
.box-item::-webkit-scrollbar {
  width: 0px;
  height: 0px;
}
</style>
